% Author: Yang Ge, geyang_xjnu@sina.com
% 
% Implemention of the fireworks algorithm
% standard fireworks algorithm
% parameters
% params.seeds = 5
% params.dimension = 30
% params.upper_bound = 20
% params.lower_bound = -20
% params.iterations = 400
% params.coef_explosion_sparks = 50
% params.coef_explosion_a = 0.04
% params.coef_explosion_b = 0.8
% params.coef_amplitude = 40
% params.mutation_number = 5;
% params.func = 
function result = FWA(params)
global seeds;
global dimension;
global upper_bound;
global lower_bound;
% for calculating the sparks number
global coef_explosion_sparks;
global coef_explosion_a;
global coef_explosion_b;
% for calculating the explosion amplitude
global coef_amplitude;
global func;

seeds = params.seeds;
dimension = params.dimension;
upper_bound = params.upper_bound;
lower_bound = params.lower_bound;
coef_explosion_sparks = params.coef_explosion_sparks;
coef_explosion_a = params.coef_explosion_a;
coef_explosion_b = params.coef_explosion_b;
coef_amplitude = params.coef_amplitude;
func = params.func;

seeds_fitness = zeros(1, seeds);
seeds_matrix = zeros(seeds, dimension);
sons_sparks_matrix = [];
mutation_sparks_matrix = [];

% randomly initialize the fire works population
seeds_matrix = rand(seeds, dimension) * (upper_bound - lower_bound) + lower_bound;
                    
% calculate the fitness of first generation 
for seed = 1:seeds
    seeds_fitness(seed) = func(seeds_matrix(seed,:));
end
result(1) = min(seeds_fitness);
for gen = 2 : params.iterations

    % for each seed using the explosion operation to generate the son sparks
    for i = 1:seeds
        % calculate the sparks of the ith firework
        sparks_number = calculate_sparks(seeds_fitness, i);
        % calculate the amplitude of the ith firework
        amplitude = calculate_amplitude(seeds_fitness, i);
        % randomly gengerate the sparks of ith firework as the sons martix
        ith_sons_sparks_matrix = generate_new_sparks(seeds_matrix(i,:), sparks_number, amplitude);
        sons_sparks_matrix = [sons_sparks_matrix; ith_sons_sparks_matrix];
    end
    
    % by using the mutation opperation to generate the mutation sparks
    for j = 1:params.mutation_number
        % randomly select a firework seed_matrix(randomi)
        random_firework_index = randi([1, seeds], 1);
        random_mutation_spark = gaussian_mutation(seeds_matrix(random_firework_index,:));
        mutation_sparks_matrix = [mutation_sparks_matrix; random_mutation_spark];
    end
    % bounds cheaking(already done)
    % by using the selection operation to form the next generation seeds.
    
    % select the best seed and keep it for next explosion generation
    all_seeds_matrix = [seeds_matrix; sons_sparks_matrix; mutation_sparks_matrix];
    next_seeds_matrix = select_next_generation(all_seeds_matrix);

    % update seeds matrix
    seeds_matrix = next_seeds_matrix;
    % update the fitness of next generation seeds
    for seed_index = 1:seeds
        seeds_fitness(seed_index) = func(next_seeds_matrix(seed_index,:));
    end
    sons_sparks_matrix = [];
    mutation_sparks_matrix = [];
    % print best fitness value of current evaluation
    fprintf('evaluation: %d, best fitness value: %.6f\n', gen, min(seeds_fitness));
    result(gen) = min(seeds_fitness);
end
end

